/* 
 * File:   main.cpp
 * Author: macos2
 *
 * Created on April 16, 2010, 3:24 PM
 */

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
//#include <IOKit/IOMessage.h>
#include <mach/mach.h>
#include <CoreFoundation/CFNumber.h>
//#include <IOKit.framework/Headers/IOKitKeys.h>

using namespace std;

void DeviceAdded(void *refCon, io_iterator_t iterator);
void DeviceRemoved(void *refCon, io_iterator_t iterator);

//int main(int argc, char** argv) {
//    kern_return_t			kr;
//    CFMutableDictionaryRef 	matchingDict = 0;		// requires <IOKit/IOKitLib.h>
//    SInt32					idInterfaceClassCode = 0xe;
//    SInt32					idVendor = 0x1c4f;
//    SInt32					idProduct = 0x3000;
//    CFNumberRef				numberRef;
//    io_iterator_t			iterator = 0;
//    io_iterator_t			itfIterator = 0;
//    io_service_t			usbDeviceRef;
//    io_name_t devName;
//    io_string_t pathName;
//
//    IOCFPlugInInterface         **plugInInterface = NULL;
//    IOUSBDeviceInterface        **dev = NULL;
//    HRESULT                     result;
//    SInt32                      score;
//    UInt16                      vendor;
//    UInt16                      product;
//
//    IOUSBFindInterfaceRequest   request;
//    io_service_t                usbInterface;
//    IOUSBInterfaceInterface     **interface = NULL;
//    UInt8                       interfaceClass;
//    UInt8                       interfaceSubClass;
//
//    matchingDict = IOServiceMatching(kIOUSBDeviceClassName);	// requires <IOKit/usb/IOUSBLib.h>
//    if (!matchingDict)
//    {
//        printf("Couldn’t create a USB matching dictionary\n");
//        mach_port_deallocate(mach_task_self(), kIOMasterPortDefault);
//        return -1;
//    }
//
//    numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idVendor);
//    if (!numberRef)
//    {
//        printf("USBSimpleExample: could not create CFNumberRef for vendor\n");
//        return -1;
//    }
//    CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef);
//    CFRelease(numberRef);
//    numberRef = 0;
//    numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idProduct);
//    if (!numberRef)
//    {
//        printf("USBSimpleExample: could not create CFNumberRef for product\n");
//        return -1;
//    }
//    CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef);
//    CFRelease(numberRef);
//    numberRef = 0;
//
//    kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iterator);
//    matchingDict = 0;			// this was consumed by the above call
//
//    while ( (usbDeviceRef = IOIteratorNext(iterator)) )
//    {
//		printf("Found device %p\n", (void*)usbDeviceRef);
//        IORegistryEntryGetName(usbDeviceRef, devName);
//        printf("Device's name = %s\n", devName);
//
//        //Create an intermediate plug-in
//        kr = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID,
//                kIOCFPlugInInterfaceID, &plugInInterface, &score);
//
//        //Don’t need the device object after intermediate plug-in is created
//        kr = IOObjectRelease(usbDeviceRef);
//
//        if ((kIOReturnSuccess != kr) || !plugInInterface)
//        {
//            printf("Unable to create a plug-in (%08x)\n", kr);
//            continue;
//        }
//
//        //Now create the device interface
//        result = (*plugInInterface)->QueryInterface(plugInInterface,
//                CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID *)&dev);
//
//        //Don’t need the intermediate plug-in after device interface is created
//        (*plugInInterface)->Release(plugInInterface);
//
//        if (result || !dev)
//        {
//            printf("Couldn’t create a device interface (%08x)\n", (int) result);
//            continue;
//        }
//        //Placing the constant kIOUSBFindInterfaceDontCare into the following
//        //fields of the IOUSBFindInterfaceRequest structure will allow you
//        //to find all the interfaces
//        request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
//        request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
//        request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
////        request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
//
//        //Get an iterator for the interfaces on the device
//        kr = (*dev)->CreateInterfaceIterator(dev, &request, &itfIterator);
//
//        while (usbInterface = IOIteratorNext(itfIterator))
//        {
//            //Create an intermediate plug-in
//            kr = IOCreatePlugInInterfaceForService(usbInterface,kIOUSBInterfaceUserClientTypeID,
//                    kIOCFPlugInInterfaceID, &plugInInterface, &score);
//
//            //Release the usbInterface object after getting the plug-in
//            kr = IOObjectRelease(usbInterface);
//
//            if ((kr != kIOReturnSuccess) || !plugInInterface)
//            {
//                printf("Unable to create a plug-in (%08x)\n", kr);
//                break;
//            }
//
//            //Now create the device interface for the interface
//            result = (*plugInInterface)->QueryInterface(plugInInterface,
//                    CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID *) &interface);
//
//            //No longer need the intermediate plug-in
//            (*plugInInterface)->Release(plugInInterface);
//
//            if (result || !interface)
//            {
//                printf("Couldn’t create a device interface for the interface (%08x)\n", (int) result);
//                break;
//            }
//
//            //Get interface class and subclass
//            kr = (*interface)->GetInterfaceClass(interface, &interfaceClass);
////            kr = (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass);
//            printf("Interface class %d\n", interfaceClass);
//        }
//    }
//
//    IOObjectRelease(iterator);
//    iterator = 0;
//    mach_port_deallocate(mach_task_self(), kIOMasterPortDefault);
//    cout << "Finished\n";
//    return (EXIT_SUCCESS);
//}
int main(int argc, char** argv) {
    kern_return_t			kr;
    CFMutableDictionaryRef 	matchingDictionary = 0;		// requires <IOKit/IOKitLib.h>
    SInt32					idInterfaceClassCode = kUSBVideoInterfaceClass; //0xe;
    SInt32					idInterfaceSubClassCode = kUSBVideoControlSubClass; //0x1;
    CFNumberRef				numberRef;

    static IONotificationPortRef    gNotifyPort;
    static io_iterator_t            gAddedIter;
    static io_iterator_t            gRemovedIter;
    static io_iterator_t            iterator;
    io_service_t usbInterface;
    CFRunLoopSourceRef      runLoopSource;
    matchingDictionary = IOServiceMatching(kIOUSBInterfaceClassName);	// requires <IOKit/usb/IOUSBLib.h>

    numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idInterfaceClassCode);
    if (!numberRef)
    {
        printf("USBSimpleExample: could not create CFNumberRef for vendor\n");
        return -1;
    }
    CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBInterfaceClass), numberRef);
    CFRelease(numberRef);
    numberRef = 0;
    numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idInterfaceSubClassCode);
    if (!numberRef)
    {
        printf("USBSimpleExample: could not create CFNumberRef for product\n");
        return -1;
    }
    CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBInterfaceSubClass), numberRef);
    CFRelease(numberRef);
    numberRef = 0;

//    kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &iterator);
//    matchingDictionary = 0;			// this was consumed by the above call

//    io_name_t devName, pathName;
//    while ( (usbInterface = IOIteratorNext(iterator)) )
//    {
//        printf("Found device %p\n", (void*)usbInterface);
//        IORegistryEntryGetName(usbInterface, devName);
//        printf("Device's name = %s\n", devName);
//        IORegistryEntryGetPath(usbInterface, kIOServicePlane, pathName);
//        printf("Device's path in IOService plane = %s\n", pathName);
//        IORegistryEntryGetPath(usbInterface, kIOUSBPlane, pathName);
//        printf("Device's path in IOUSB plane = %s\n", pathName);
//        printf("\n\n");
//    }

    //To set up asynchronous notifications, create a notification port and
    //add its run loop event source to the program’s run loop
    gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);
    runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);

    //Retain additional dictionary references because each call to
    //IOServiceAddMatchingNotification consumes one reference
    matchingDictionary = (CFMutableDictionaryRef) CFRetain(matchingDictionary);
    matchingDictionary = (CFMutableDictionaryRef) CFRetain(matchingDictionary);

    //Now set up two notifications: one to be called when a raw device
    //is first matched by the I/O Kit and another to be called when the
    //device is terminated

    //Notification of first match:
    kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification, matchingDictionary,
            DeviceAdded, NULL, &gAddedIter);

    //Iterate over set of matching devices to access already-present devices
    //and to arm the notification
    DeviceAdded(NULL, gAddedIter);

    //Notification of termination:
    kr = IOServiceAddMatchingNotification(gNotifyPort, kIOTerminatedNotification, matchingDictionary,
            DeviceRemoved, NULL, &gRemovedIter);

    //Iterate over set of matching devices to release each one and to
    //arm the notification
    DeviceRemoved(NULL, gRemovedIter);

    //Finished with master port
    mach_port_deallocate(mach_task_self(), kIOMasterPortDefault);

    //Start the run loop so notifications will be received
    CFRunLoopRun();

    //Because the run loop will run forever until interrupted,
    //the program should never reach this point

    return 0;
}

void DeviceAdded(void *refCon, io_iterator_t iterator)
{
//    kern_return_t kr;
//    IOCFPlugInInterface **plugInInterface = NULL;
//    IOUSBInterfaceInterface **interface = NULL;
//    SInt32 score;
//    UInt8 interfaceClass;
//    UInt8 interfaceSubClass;
//    HRESULT result;
    io_service_t usbInterface;
    bool bRes = false;
    io_name_t devName, pathName;

    while ( (usbInterface = IOIteratorNext(iterator)) )
    {
//        printf("Found device %p\n", (void*)usbInterface);
//        IORegistryEntryGetName(usbInterface, devName);
//        printf("Device's name = %s\n", devName);
//        IORegistryEntryGetPath(usbInterface, kIOServicePlane, pathName);
//        printf("Device's path in IOService plane = %s\n", pathName);
//        IORegistryEntryGetPath(usbInterface, kIOUSBPlane, pathName);
//        printf("Device's path in IOUSB plane = %s\n", pathName);
//
//        kr = IOCreatePlugInInterfaceForService(usbInterface, kIOUSBInterfaceUserClientTypeID,
//                    kIOCFPlugInInterfaceID, &plugInInterface, &score);
//
//        //Release the usbInterface object after getting the plug-in
//        kr = IOObjectRelease(usbInterface);
//
//        if ((kr != kIOReturnSuccess) || !plugInInterface)
//        {
//            printf("Unable to create a plug-in (%08x)\n", kr);
//            break;
//        }
//
//        //Now create the device interface for the interface
//        result = (*plugInInterface)->QueryInterface(plugInInterface,
//                CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID *) &interface);
//
//        //No longer need the intermediate plug-in
//        (*plugInInterface)->Release(plugInInterface);
//
//        if (result || !interface)
//        {
//            printf("Couldn’t create a device interface for the interface (%08x)\n", (int) result);
//            break;
//        }
//
//        //Get interface class and subclass
//        kr = (*interface)->GetInterfaceClass(interface, &interfaceClass);
//        kr = (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass);
//        printf("Interface class %d\n", interfaceClass);
//        printf("Interface subclass %d\n", interfaceSubClass);
//
//        if (interfaceClass == 0xe)
//        {
//            printf("2Webcam plugged in.\n");
//        }
//        printf("entered DeviceAdded while loop\n");
        printf("one webcam detected.\n");
        bRes = true;
        IOObjectRelease(usbInterface);
    }
    if (bRes) printf("Webcam plugged in.\n");
}

void DeviceRemoved(void *refCon, io_iterator_t iterator)
{
//    kern_return_t kr;
//    IOCFPlugInInterface **plugInInterface = NULL;
//    IOUSBInterfaceInterface **interface = NULL;
//    SInt32 score;
//    UInt8 interfaceClass;
//    UInt8 interfaceSubClass;
//    HRESULT result;
    printf("entered DeviceRemoved\n");
    io_service_t usbInterface;
    bool bRes = false;
//    io_name_t devName, pathName;
//
//    printf("DeviceAdded called\n");
//
    while ( (usbInterface = IOIteratorNext(iterator)) )
    {
//		printf("Found device %p\n", (void*)usbInterface);
//        IORegistryEntryGetName(usbInterface, devName);
//        printf("Device's name = %s\n", devName);
//        IORegistryEntryGetPath(usbInterface, kIOServicePlane, pathName);
//        printf("Device's path in IOService plane = %s\n", pathName);
//        IORegistryEntryGetPath(usbInterface, kIOUSBPlane, pathName);
//        printf("Device's path in IOUSB plane = %s\n", pathName);
//
//        kr = IOCreatePlugInInterfaceForService(usbInterface, kIOUSBInterfaceUserClientTypeID,
//                    kIOCFPlugInInterfaceID, &plugInInterface, &score);
//
//        //Release the usbInterface object after getting the plug-in
//        kr = IOObjectRelease(usbInterface);
//
//        if ((kr != kIOReturnSuccess) || !plugInInterface)
//        {
//            printf("Unable to create a plug-in (%08x)\n", kr);
//            break;
//        }
//
//        //Now create the device interface for the interface
//        result = (*plugInInterface)->QueryInterface(plugInInterface,
//                CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID *) &interface);
//
//        //No longer need the intermediate plug-in
//        (*plugInInterface)->Release(plugInInterface);
//
//        if (result || !interface)
//        {
//            printf("Couldn’t create a device interface for the interface (%08x)\n", (int) result);
//            break;
//        }
//
//        //Get interface class and subclass
//        kr = (*interface)->GetInterfaceClass(interface, &interfaceClass);
//        kr = (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass);
//        printf("Interface class %d\n", interfaceClass);
//        printf("Interface subclass %d\n", interfaceSubClass);
//
//        if (interfaceClass == 0xe)
//        {
//            printf("Webcam plugged out.\n");
//        }
        printf("one webcam removed\n");
        bRes = true;
        IOObjectRelease(usbInterface);
    }
    if (bRes) printf("Webcam plugged out.\n");
}


    
//    IOObjectRelease(iterator);
//    iterator = 0;
//
//    mach_port_deallocate(mach_task_self(), kIOMasterPortDefault);
//    cout << "Finished\n";
//    return (EXIT_SUCCESS);


